#include <Precomp.h>
#include "Application.h"
#include "ClientListener.h"
#include <SoundManager.h>

#include <D3DUIRenderer.h>
#include <UIManager.h>
#include <AVUIResourceDictionary.h>
#include <Radar.h>
#include <DetailViewWindow.h>
#include <UIRenderer.h>

using namespace Ogre;
using namespace AVUI;

CNetrekClientApplication::CNetrekClientApplication() : 
      m_pServerConnection(NULL), 
      m_pRoot(NULL),
      m_pFrameListener(NULL),
      m_pWindow(NULL),
      m_pSoundManager(NULL),
      m_pUIRenderer(NULL),
      m_pUIApplication(NULL)
{
    CoInitialize(NULL);
}

CNetrekClientApplication::~CNetrekClientApplication()
{
    if(m_pFrameListener != NULL)
    {
        ((CNetrekClientListener*) m_pFrameListener)->Cleanup();

        delete m_pFrameListener;
        m_pFrameListener = NULL;
    }

    if(m_pUIApplication != NULL)
    {
        Application::set_Current(NULL);

        delete m_pUIApplication;
        m_pUIApplication = NULL;
    }

    if(m_pUIRenderer != NULL)
    {
        delete m_pUIRenderer;
        m_pUIRenderer = NULL;
    }

    if(m_pSoundManager != NULL)
    {
        m_pSoundManager->Cleanup();
        delete m_pSoundManager;
        m_pSoundManager = NULL;
    }

    if(m_pRoot != NULL)
    {
        delete m_pRoot;
        m_pRoot = NULL;
    }
}


// App.Run is main entry point to application
void CNetrekClientApplication::Run()
{
    if (!Setup())
        return;

    m_pRoot->startRendering();

}

WNDPROC g_oldWndProc = NULL;
CNetrekClientListener* g_pListener = NULL;
bool g_isDeviceLost = false;

LRESULT WndProcSubclass(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    if(uMsg == WM_CHAR)
    {
        g_pListener->CharEntered((WCHAR) wParam);
    }
    else if(uMsg == WM_KEYDOWN)
    {
        g_pListener->KeyDown((WCHAR) wParam);
    }
    else if(uMsg == WM_KEYUP)
    {
        g_pListener->KeyUp((WCHAR) wParam);
    }

    return CallWindowProc(g_oldWndProc, hwnd, uMsg, wParam, lParam);
}

void SubclassWndProc()
{
    HWND hwndWindow = GetActiveWindow();

    SetClassLong(hwndWindow, GCL_HCURSOR, NULL);

    LONG_PTR wndProcValue = GetWindowLongPtr(hwndWindow, GWLP_WNDPROC);
    g_oldWndProc = (WNDPROC) wndProcValue;
    SetWindowLong(hwndWindow, GWL_WNDPROC, (LONG)WndProcSubclass);
}

void* AllocateUIMemory(size_t size)
{
    int* piMem = (int*) malloc(size + 4);

    *piMem = size;

    return piMem + 1;
}

void FreeUIMemory(void* p)
{
    int* piMem = ((int*) p) - 1;

    memset(piMem, 0xFE, *piMem);

    free(piMem);
}


class PlayerEntry;
bool CNetrekClientApplication::Setup()
{
    m_pRoot = new Root();                                    

    SetupResources();
                                                                        
    bool fContinue = ConfigureRenderOptions(); // This will go away eventually
    if (!fContinue)                                     
        return false;                                   
                                                             
    m_pSoundManager = new CSoundManager();                   
    m_pSoundManager->Initialize();                           

    // Set default mipmap level (NB some APIs ignore this)
    Ogre::TextureManager::getSingleton().setDefaultNumMipmaps(5);

    // Initialise, parse scripts etc
    ResourceGroupManager::getSingleton().initialiseAllResourceGroups();

    Application::RegisterMemoryAllocator(&AllocateUIMemory, &FreeUIMemory);

    m_pUIApplication = new AVUI::Application();

    m_pRoot->createSceneManager(ST_GENERIC, "Default");

    IDirect3DDevice9* pDevice;

    m_pWindow->getCustomAttribute("D3DDEVICE",&pDevice);
    m_pUIRenderer = new CustomUIRenderer(m_pSoundManager, pDevice);           
    m_pUIRenderer->set_ClearMainSurfaceOnRender(false);
    m_pUIApplication->set_ClientRendererInterface(m_pUIRenderer);


    ResourceDictionary* pResources = CUIManager::LoadXamlResources(L"Media\\Xaml\\SystemResources.xaml");

    m_pUIApplication->set_SystemResources(pResources);
    pResources->Release();
    pResources = NULL;

    m_pFrameListener= new CNetrekClientListener(this);                   
                                                  
    g_pListener = (CNetrekClientListener*) m_pFrameListener;
    ((CNetrekClientListener*) m_pFrameListener)->Initialize();

    m_pRoot->addFrameListener(m_pFrameListener);                                 
                                          
    SubclassWndProc();

    REGISTER_TYPE(Radar);
    AVUI::Application::RegisterType(GET_TYPE(Radar));        

    REGISTER_TYPE(DetailViewWindow);
    AVUI::Application::RegisterType(GET_TYPE(DetailViewWindow));

    REGISTER_TYPE(PlayerEntry);    

    return true;
}

bool CNetrekClientApplication::ConfigureRenderOptions()
{
    RenderSystem* rs = m_pRoot->getRenderSystemByName("Direct3D9 Rendering Subsystem");

    if (!rs)
    {
        return false;
    }                                                        

    rs->setConfigOption("Full Screen", "false");
    m_pRoot->setRenderSystem(rs);

    m_pWindow = m_pRoot->initialise(true, "NetrekNG");

    const RenderSystemCapabilities* pCaps = rs->getCapabilities();

    bool hasVertexShader = pCaps->hasCapability(RSC_VERTEX_PROGRAM);
    bool hasPixelShader = pCaps->hasCapability(RSC_FRAGMENT_PROGRAM);

    if(!hasVertexShader || !hasPixelShader)
    {
        // Pixel shader required
        MessageBoxW(NULL, L"Vertex/Pixel Shader required.", L"Initialization Error", MB_OK);
        return false;
    }

    static class RenderSystemListener : public Ogre::RenderSystem::Listener
    {
    public:
        RenderSystemListener(CNetrekClientApplication* pApp) : m_pApp(pApp) { }
        virtual void eventOccurred(const Ogre::String& eventName, const Ogre::NameValuePairList* parameters = 0)
        {
            if(eventName == "DeviceLost")
            {
                IDirect3DDevice9* pDevice;
                m_pApp->GetRenderWindow()->getCustomAttribute("D3DDEVICE",&pDevice);

                pDevice->SetRenderTarget(0, NULL);

                m_pApp->GetUIRenderer()->DeviceLost();
                g_isDeviceLost = true;
            }
            else if(eventName == "DeviceRestored")
            {
                IDirect3DDevice9* pDevice;
                m_pApp->GetRenderWindow()->getCustomAttribute("D3DDEVICE",&pDevice);

                m_pApp->GetUIRenderer()->DeviceReset(pDevice);
                g_isDeviceLost = false;
            }
        }

        CNetrekClientApplication* m_pApp;
    } rsListener(this);
    
    rs->addListener(&rsListener);

    return true;
}

void CNetrekClientApplication::SetupResources(void)
{
    ResourceGroupManager::getSingleton().addResourceLocation("Media\\Packs\\OgreCore.zip", "Zip", "General");
    ResourceGroupManager::getSingleton().addResourceLocation("Media\\UI", "FileSystem", "General");
    ResourceGroupManager::getSingleton().addResourceLocation("Media\\Materials\\Scripts", "FileSystem", "General");
    ResourceGroupManager::getSingleton().addResourceLocation("Media\\Materials\\Textures", "FileSystem", "General");
    ResourceGroupManager::getSingleton().addResourceLocation("Media\\Meshes", "FileSystem", "General");
    ResourceGroupManager::getSingleton().addResourceLocation("Media\\Ships\\Fed", "FileSystem", "General");
    ResourceGroupManager::getSingleton().addResourceLocation("Media\\Ships\\Rom", "FileSystem", "General");
    ResourceGroupManager::getSingleton().addResourceLocation("Media\\Ships\\Kli", "FileSystem", "General");
    ResourceGroupManager::getSingleton().addResourceLocation("Media\\Ships\\Ori", "FileSystem", "General");
    ResourceGroupManager::getSingleton().addResourceLocation("Media\\Ships\\Ind", "FileSystem", "General");
    ResourceGroupManager::getSingleton().addResourceLocation("Media\\Fonts", "FileSystem", "General");
}                                                            


